package io.yx.flx;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.db.sql.SqlExecutor;
import cn.hutool.http.Header;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.setting.Setting;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.FileSystemAccess;
import io.vertx.ext.web.handler.StaticHandler;
import io.yx.flx.enums.CompressEnum;
import io.yx.flx.handler.IndexHandler;
import io.yx.flx.handler.SignHandler;
import io.yx.flx.handler.TodayFileTotalHandler;
import io.yx.flx.handler.TokenHandler;
import io.yx.flx.handler.upload.UploadNoNameHandler;
import io.yx.flx.handler.upload.UploadNotCompressHandler;
import io.yx.flx.handler.upload.UploadReturnNameHandler;
import io.yx.flx.task.DiskMonitor;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Main {

    private static final String envFileName = "env.setting";
    private static final Log log = LogFactory.get();
    public static final Setting setting;

    public static CompressEnum compressMethod = CompressEnum.thumbnails;

    static {
        byte[] bytes = ResourceUtil.readBytes(envFileName);
        String targetEnvFilePath = getBasePath() + File.separator + envFileName;
        if (!FileUtil.exist(targetEnvFilePath)) {
            File file = FileUtil.file(targetEnvFilePath);
            FileUtil.writeBytes(bytes, file);
        }
        String envFilePath = getBasePath() + File.separator + envFileName;
        setting = new Setting(envFilePath);
        Boolean openOSS = setting.getBool("openOSS");
        if (Boolean.TRUE.equals(openOSS)) {
            Setting ossSetting = new Setting(setting.getStr("ossConfigPath"));
            Set<Map.Entry<String, String>> entries = ossSetting.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                System.setProperty(entry.getKey(), entry.getValue());
            }
        }

        // 设置压缩方式
        String compress = setting.getStr("compress");
        log.info("压缩配置为:{}", compress);
        if (StrUtil.equalsIgnoreCase("opencv", compress)) {
            compressMethod = CompressEnum.opencv;
        } else {
            compressMethod = CompressEnum.thumbnails;
        }

    }


    public static void main(String[] args) {

        log.debug("PID:{}", RuntimeUtil.getPid());

        Vertx vertx = Vertx.vertx();

        HttpServer httpServer = vertx.createHttpServer(openSSL());

        Router router = Router.router(vertx);

        // 允许最大上传的文件大小通过setBodyLimit设置
        router.route().handler(BodyHandler.create()
                .setBodyLimit(5L * 1024 * 1024 * 1024)
                .setDeleteUploadedFilesOnEnd(true));

        router.route().handler(CorsHandler.create().addOrigin("*"));

        StaticHandler staticHandler = StaticHandler.create("static").setDefaultContentEncoding(StandardCharsets.UTF_8.displayName())
                .setCachingEnabled(true)
                .setMaxAgeSeconds(86400)// 缓存1天
                .setIndexPage("index.html");

        router.route("/").handler(staticHandler);

        router.route("/systemInfo").handler(new IndexHandler());

        router.route("/finalize").handler((rc) -> {
            System.gc();
            rc.end();
        });

        router.route("/todayFileTotal").handler(new TodayFileTotalHandler());


        // 所有static都路由到
        router.route("/static/*").handler(staticHandler);

        router.route("/sign/getSign").handler(new SignHandler());

        router.route("/token/getToken").handler(new TokenHandler());

        router.route("/storage/*").handler(StaticHandler.create(FileSystemAccess.ROOT, BodyHandler.DEFAULT_UPLOADS_DIRECTORY)// 设置web-root目录
//                .setDirectoryListing(true)// 是否启用目录列表
                .setCachingEnabled(false));// 是否缓存文件信息


        // 上传的子路由
        Router uploadSubRouter = Router.router(vertx);
        uploadSubRouter.route("/upload").blockingHandler(new UploadNoNameHandler());// 此路由路径为/upload
        uploadSubRouter.route("/uploadImgs").blockingHandler(new UploadReturnNameHandler());// 此路由路径为/uploadImgs
        uploadSubRouter.route("/uploadNotCompress").blockingHandler(new UploadNotCompressHandler());// 此路由路径为/uploadNotCompress
        // 挂载上传的子路由
        router.route().subRouter(uploadSubRouter);

        httpServer.requestHandler(router).listen().onSuccess(handler -> {
            int port = handler.actualPort();
            log.info("使用端口:{}", port);
        }).onFailure(handler -> {
            log.error("启动失败：" + handler.getMessage());
        });
        // 初始化
        initDatabases();

        // 启动磁盘空间监控线程
        DiskMonitor.cacheClean();

    }

    /**
     * 获取项目的根目录
     */
    public static String getBasePath() {
        try {
            return new File("").getCanonicalPath();
        } catch (IOException e) {
            log.error("获取目录失败", e);
            return null;
        }
    }

    /**
     * 初始化数据库
     */
    public static void initDatabases() {

        String sqlFileContent = ResourceUtil.readUtf8Str("initTable.sql");
        List<String> sqlPart = StrUtil.split(sqlFileContent, ";");
        try {
            DataSource dataSource = DSFactory.get();
            Connection connection = dataSource.getConnection();
            for (String s : sqlPart) {
                if (StrUtil.isNotBlank(s)) {
                    SqlExecutor.execute(connection, s);
                }
            }
        } catch (Exception e) {
            log.error("获取连接失败", e);
        }

    }

    /**
     * 开启ssl
     */
    public static HttpServerOptions openSSL() {
        // 读取类路径中的配置文件并输出到项目同级目录
        Boolean openSSL = setting.getBool("openSSL");
        Integer port = setting.getInt("port");
        if (openSSL) {
            return new HttpServerOptions().setSsl(true).setPemKeyCertOptions(
                    new PemKeyCertOptions().
                            setCertPath(setting.getStr("certPath")).
                            setKeyPath(setting.getStr("keyPath"))
            ).setClientAuth(ClientAuth.REQUEST).setPort(port);

        } else {
            return new HttpServerOptions().setPort(port);
        }

    }

}
